package cn.edu.hitsz.compiler.lexer;

import cn.edu.hitsz.compiler.NotImplementedException;
import cn.edu.hitsz.compiler.symtab.SymbolTable;
import cn.edu.hitsz.compiler.utils.FilePathConfig;
import cn.edu.hitsz.compiler.utils.FileUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.StreamSupport;

/**
 * TODO: 实验一: 实现词法分析
 * <br>
 * 你可能需要参考的框架代码如下:
 *
 * @see Token 词法单元的实现
 * @see TokenKind 词法单元类型的实现
 */
public class LexicalAnalyzer {
    private final SymbolTable symbolTable;

    private List<String> codes;

    private final List<Token> tokenList = new ArrayList<>();

    public LexicalAnalyzer(SymbolTable symbolTable) {
        this.symbolTable = symbolTable;
    }

    /**
     * 向 token 中添加标识符
     * @param id
     */
    private void addId(String id)
    {
        if ("int".equals(id) || "return".equals(id))
        {
            tokenList.add(Token.simple(id));
        }
        else
        {
            tokenList.add(Token.normal("id", id));
            if (!symbolTable.has(id))
            {
                symbolTable.add(id);
            }
        }
    }

    /**
     * 从给予的路径中读取并加载文件内容
     *
     * @param path 路径
     */
    public void loadFile(String path)
    {
        codes =  FileUtils.readLines(path);
    }

    /**
     * 执行词法分析, 准备好用于返回的 token 列表 <br>
     * 需要维护实验一所需的符号表条目, 而得在语法分析中才能确定的符号表条目的成员可以先设置为 null
     */
    public void run()
    {
        enum State
        {
            blank, digit, iden;
        }

        for (String code : codes)
        {
            int start = 0;
            int tail = 0;
            char ch;
            State state = State.blank;

            int len = code.length();

            while (start < len && tail < len)
            {
                ch = code.charAt(tail);
                if (Character.isDigit(ch))
                {
                    switch (state)
                    {
                        case blank:
                        {
                            state = State.digit;
                            break;
                        }
                        case digit:
                            break;
                        case iden:
                            //throw new RuntimeException("标识符不允许出现数字");
                            break;
                        default:
                            throw new RuntimeException("未知错误");
                    }
                }
                else if (Character.isLetter(ch) || ch == '_')
                {
                    switch (state)
                    {
                        case blank:
                            state = State.iden;
                            break;
                        case digit:
                            throw new RuntimeException("数字中出现非数字字符");
                        case iden:
                            if (ch == '_')
                            {
                                throw new RuntimeException("‘_’ 只能出现在标识符首位");
                            }
                            break;
                        default:
                            throw new RuntimeException("未知错误");
                    }
                }
                else
                {
                    switch (state)
                    {
                        case blank:
                            break;
                        case digit:
                            tokenList.add(Token.normal("IntConst", code.substring(start, tail)));
                            state = State.blank;
                            break;
                        case iden:
                            String id = code.substring(start, tail);
                            addId(id);
                            state = State.blank;
                            break;
                        default:
                            throw new RuntimeException("未知错误");
                    }
                    if (ch == ';')
                        tokenList.add(Token.simple("Semicolon"));
                    else if (ch == '=' || ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == ',' || ch == '(' || ch == ')')
                        tokenList.add(Token.simple(code.substring(tail, tail+1)));
                    start = tail+1;
                }
                tail++;
            }

            if (start < tail)
            {
                switch (state)
                {
                    case blank:
                        break;
                    case digit:
                        tokenList.add(Token.normal("IntConst", code.substring(start, tail)));
                        state = State.blank;
                        break;
                    case iden:
                        String id = code.substring(start, tail);
                        addId(id);
                        state = State.blank;
                        break;
                    default:
                        throw new RuntimeException("未知错误");
                }
            }
        }
        tokenList.add(Token.eof());
    }

    /**
     * 获得词法分析的结果, 保证在调用了 run 方法之后调用
     *
     * @return Token 列表
     */
    public Iterable<Token> getTokens()
    {
        return new Iterable<Token>() {
            @Override
            public Iterator<Token> iterator() {
                return tokenList.iterator();
            }
        };
    }

    public void dumpTokens(String path)
    {
        FileUtils.writeLines(
            path,
            StreamSupport.stream(getTokens().spliterator(), false).map(Token::toString).toList()
        );
    }
}
